单元1 搭建场景 本教程将会带大家实现一款经典的小游戏 |
您所在的位置:网站首页 › sans像素图 方格 › 单元1 搭建场景 本教程将会带大家实现一款经典的小游戏 |
单元1 搭建场景 本教程将会带大家实现一款经典的小游戏——像素鸟,英文名Flappy Bird。这是一款休闲动作游戏,玩家通过点击屏幕控制小鸟的起飞降落,躲开沿途的障碍物。游戏没有胜利,玩家通过创建新的最高分而不断地挑战自我。 任务1.1 导入美术资源包 1. 创建工程 打开Unity的可执行程序(本案例使用的是Unity2017.2.0f3),选择创建2D工程,如图所示。
2. 导入资源包 找到教程提供的资源包,如图所示。直接拖入到Unity的Project视图中。
3. 设置图片格式 如果创建工程选择了2D模式,那么系统会自动将导入的图片设置为Sprite类型,此步骤可忽略不用操作。如果选择了3D模式,需要手动设置。在Project视图中选中所有的图片,如图所示。
在右侧Inspector视图中,将Texture Type设置为Sprite(2D and UI),最后不要忘记单击下方的“Apply”按钮,如图所示。
任务1.2 创建背景 1. 新建场景 按“Ctrl+S”键,或者选择File→Save Scene命令,将场景命名为Game,保存。此时场景里只保留Camera一个游戏对象,如图所示。
2. 调整相机 如果创建工程的时候选择了3D模式,则需要调整相机的设置。选中Camera,在Camera组件里设置Projection为Orthographic,Size设置为1.75。 在Game视图中设置默认分辨率为1440*900,如图所示。
3. 添加大地 在image文件夹里找到back图片,这是我们的地面。直接拖拽到场景中,然后再复制2个,分别命名为back0、back1、back2。如图所示。
将它们的坐标分别设置为(-0.25,0,0)、(4.8,0,0)、(9.85,0,0),此时的场景应该是这样的。
接着,我们需要创建一个空游戏对象,命名为Ground,修改坐标为(0,0,0),然后将3个back都放到Ground下,成为其子物体,如图所示。
这样一来,在实现无尽地图时会比较方便。 4. 添加天空 在image文件夹里找到bg,拖拽到场景中,同样再复制2个,命名为bg0、bg1、bg2。将他们的坐标分别设置为(-0.95,0,0)、(2.07,0,0)、(5.09,0,0)。同样新建一个空物体,命名为Sky,将3个bg对象都放到Sky下,成为其子物体。此时的场景应该是这样的。
天空遮住了地面,不用着急。选中3个bg对象,在Inspector的Sprite Renderer组件里修改Sorting Layer为-2。最终效果如图所示。
任务1.3 添加水管 1. 添加两根水管 从image文件夹中将pipe图片拖拽到场景中,命名为pipeUp,然后复制1个,命名为pipeDown,这就是我们的两个水管。将两个水管的Sprite Renderer中sorting layer修改为-1,让其介于地面和天空之间,如图所示。
将pipeUp的坐标修改为(0,-3,0),pipeDown的坐标修改为(0,3,0),效果如图所示。
2. 复制水管 为了表示这两个水管是一个整体,让PipeUp成为pipedown的子物体,如图所示。
将PipeDown复制出2个,命名为PipeDown1和PipeDown2。最后,设置PipeDown0的坐标为(3,-3,0),PipeDown1的坐标为(6,-3,0),PipeDown2的坐标为(9,-3,0),最后的效果如图所示。
3. 统一管理 同样的,为了方便后面生成无数个管子,我们需要创建一个空游戏对象,命名为GateGroup,修改坐标为(0,0,0),将三个PipeDown放到GateGroup下面,成为其子物体,如图所示。
最后,不要忘记按“Ctrl+S”键保存场景。 单元2 会飞的小鸟 场景已经搭建完成了,接下来该主角登场了。 任务2.1 创建小鸟 1. 添加小鸟游戏对象 将image文件夹里bird拖到场景中,重命名为Bird,可以看到效果如图。
这是因为bird图片的默认格式为Single。 2. 设置bird图片格式 在Project视图选中bird图片,在Inspector视图修改Sprite Mode为Multiple,然后点击Sprite Editor按钮,进入Sprite Editor窗口,对bird图片进行切割,如图所示。
设置三个方框的Position,分别如下图所示。
设置好后,单击Sprite Editor窗口中的“Apply”按钮,应用修改并关闭窗口。 最后,在Inspector视图下方,单击“Apply”按钮,应用对图片格式的修改。此时,Scene场景中的小鸟恢复了正常,如图所示。
任务2.2 起飞降落 1. 添加刚体 在Hierarchy视图中选中Bird,在Inspector视图中单击“Add Component”按钮添加Rigidbody 2D组件。设置Gravity Scale值为0.7,如图所示。
运行游戏,可以看到小鸟受重力影响而降落。 2. 添加SportCtrl.cs 创建SportCtrl.cs脚本,并添加到Bird游戏对象上。打开SportCtrl脚本,添加Rigidbody2D的字段并在Start方法中引用自身,如图所示。 using System.Collections; using System.Collections.Generic; using UnityEngine;
public class SportCtrl : MonoBehaviour {
Rigidbody2D rb; void Start() { rb = GetComponent(); }
// Update is called once per frame void FixedUpdate() { Fly();
}
public void Fly() { float xSpeed = 1f; Vector3 v = rb.velocity; float ySpeed = v.y; if (Input.GetMouseButton(0)) { ySpeed = 2f; } rb.velocity = new Vector2(xSpeed, ySpeed); } } 再运行游戏试一试,已经可以控制小鸟起飞降落了。 任务2.3 碰撞 现在小鸟已经可以飞了,但是会穿过地面和管子,这一节将会解决这个问题。 1. 添加小鸟的碰撞体 在Bird游戏对象上添加Circle Collider 2D,半径设置为0.18,如图所示。
2. 添加大地的碰撞体 给Ground下的三个子物体back0、back1、back2添加Box Collider 2D组件,并设置成如图所示的配置。
运行游戏,可以看到小鸟已经可以碰到地面了,如图所示。
3. 添加管子的碰撞体 选中所有的PipeDown和PipUp,如图所示。
全部都添加上Box Collider 2D组件即可。 单元3 无尽模式 任务3.1 相机跟随 1. 创建CamCtrl.cs脚本 在Main Camera上添加新脚本CamCtrl.cs,并完成以下代码。 public class CamCtrl : MonoBehaviour {
public Transform target; Vector3 offset; // Use this for initialization void Start () { offset = transform.position-target.position; } // Update is called once per frame void LateUpdate () { transform.position = target.position + offset; } } 回到编辑器,对Target进行赋值,如图所示。
运行游戏,发现我们的相机已经可以跟着小鸟移动了。 2. 限制相机高度 相机有时会穿帮,需要限制高度。修改脚本的LateUpdate方法如下。 void LateUpdate () { Vector3 pos= target.position + offset; if (pos.y > 0.9) pos.y = 0.9f; else if (pos.y < -0.9) pos.y = -0.9f; transform.position = pos; } 任务3.2 无限地图 我们的思路是,将有限的图片进行重复使用,从而实现无限地图的效果。当最左侧的图片不可见时,将其移动到最右侧。 1. 创建Endless.cs脚本 添加如下代码。 using System.Collections; using System.Collections.Generic; using UnityEngine;
public class Endless : MonoBehaviour { public float distance; void OnBecameInvisible(){ transform.Translate(Vector3.right* distance * 3); } } 2. 实现无限地面 然后将脚本添加到back0、back1、back2,并在编辑器中设置distance为5.05。如图所示。
运行游戏进行测试,注意测试的时候需要关闭Scene视图。可以看到地面已经可以无限延长了,如图所示。
3. 实现无限天空 同理,给bg0、bg1、bg2添加Endless脚本,并设置distance为3.02。 运行游戏测试,可以看到如图效果。
4. 实现无限水管 同理,给pipeDown0、pipeDown1、pipeDown2添加Endless脚本,并设置distance为3。 运行游戏测试,可以看到水管也有无限个了。 任务3.3 水管随机高度 1. 创建RandomHeight.cs脚本 创建RandomHeight.cs脚本,添加如下代码。 using System.Collections; using System.Collections.Generic; using UnityEngine;
public class RandomHeight : MonoBehaviour { void OnBecameVisible() { Vector3 pos = transform.position; pos.y = Random.Range(-3f, -1.5f); transform.position = pos; } } 2. 添加脚本 选中所有的pipeDown,全都添加RandomHeight脚本。 运行游戏,测试效果。可以看到管子已经可以产生随机高度了。
单元4 游戏逻辑 任务4.1 UI 1. 创建开始界面 在场景中创建画布Canvas,然后制作开始界面。 首先在Canvas下新建一个空物体,命名为StartWnd。然后在StartWnd下新建一个Image,命名为imgTitle,SourceImage使用main图片。然后在StartWnd下新建一个Button,命名为btnStart,SourceImage使用start图片。如图所示。
调整位置,最后效果如图所示。
2. 创建准备界面 隐藏StartWnd,在Canvas下新建一个空物体,命名为ReadyWnd。然后在ReadyWnd下新建一个Button,命名为page1,SourceImage使用tap图片,效果如图所示。
隐藏page1,然后在ReadyWnd下新建一个Image,命名为page2,SourceImage使用ready图片。如图所示。
调整位置,效果如图所示。
3. 创建结束界面 隐藏ReadyWnd,在Canvas下新建一个空物体,命名为EndWnd。然后在EndWnd下新建一个Button,命名为page1,SourceImage使用gameover图片,效果如图所示。
隐藏page1,然后在EndWnd下新建一个空游戏对象,命名为page2。在page2下新建一个Image,命名为bg,SourceImage使用score图片。在bg下新建两个Text,一个命名为txtScore,一个命名为txtBest。在page2下新建一个Button,命名为btnRestart,SourceImage使用start图片,如图所示。
调整样式和位置,最后效果如图所示。
4. 创建左上角得分显示 在Canvas下新建Text,命名为txtScore。调整参数,最后效果如图所示。
任务4.2 创建游戏管理器 1. 创建GameRoot.cs脚本单例 创建空游戏对象GameRoot,并添加GameRoot.cs脚本。代码如下。 public class GameRoot : MonoBehaviour { public static GameRoot Instance; void Start() { Instance = this; } } 2. 创建游戏状态 public const int GAMESTART = 0; public const int GAMEREADY = 1; public const int GAMERUN = 2; public const int GAMEEND = 3; public int GAMESTATE = GAMESTART; 3. 引用UI窗口和主角 添加引用UI窗口和主角的字段,代码如下。 public Transform StartWnd, ReadyWnd, EndWnd, bird; public Text txtScore; public int score=0; 在外部对这些字段进行赋值,如图所示。
4. 创建游戏状态变更的方法 /// /// 刚进入游戏时 /// public void Enter() { StartWnd.gameObject.SetActive(true); GAMESTATE = GAMESTART; }
/// /// 进入准备状态时 /// public void Ready() { bird.position = Vector3.zero; StartWnd.gameObject.SetActive(false); ReadyWnd.gameObject.SetActive(true); GAMESTATE = GAMEREADY; bird.rotation = Quaternion.identity; } /// /// 进入娱乐状态时 /// public void Run() { ReadyWnd.gameObject.SetActive(false); GAMESTATE = GAMERUN; } /// /// 游戏结束时 /// public void End() { EndWnd.gameObject.SetActive(true); GAMESTATE = GAMEEND;
} 任务4.3 UI逻辑 1. 添加StartWnd脚本 首先创建StartWnd.cs脚本,代码如下: using UnityEngine; using UnityEngine.UI;
public class StartWnd : MonoBehaviour { public Button btnStart; // Use this for initialization void Start() { btnStart.onClick.AddListener(OnStartClick);
}
void OnStartClick() { GameRoot.Instance.Ready(); } } 将脚本添加到StartWnd上,然后对UI字段进行赋值。
2. 添加ReadyWnd脚本 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;
public class ReadyWnd : MonoBehaviour {
public Button page1; public Transform page2; // Use this for initialization void Start() { page1.onClick.AddListener(OnClick); } void OnEnable() { page1.gameObject.SetActive(true); page2.gameObject.SetActive(false); }
public void OnClick() { //显示page2 page1.gameObject.SetActive(false); page2.gameObject.SetActive(true); //1秒过后调用 StartCoroutine(Run()); }
IEnumerator Run() { yield return new WaitForSeconds(1); GameRoot.Instance.Run();
}
} 将脚本添加到ReadyWnd上,并对外部引用进行赋值。如图所示。
3. 添加EndWnd脚本 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;
public class EndWnd : MonoBehaviour {
public Button page1; public Transform page2; public Button btnRestart; public Text txtScore, txtBest; // Use this for initialization void Start() { page1.onClick.AddListener(OnPage1Click); btnRestart.onClick.AddListener(OnRestartClick);
} void OnEnable() { page1.gameObject.SetActive(true); page2.gameObject.SetActive(false); }
// Update is called once per frame void Update() {
}
void OnPage1Click() { page2.gameObject.SetActive(true); page1.gameObject.SetActive(false); txtBest.text = PlayerPrefs.GetInt("best").ToString(); txtScore.text = GameRoot.Instance.score.ToString(); } void OnRestartClick() { gameObject.SetActive(false); GameRoot.Instance.Ready(); }
} 将脚本添加到EndWnd上,并给外部引用赋值,如图所示。 任务4.4 得分 1. 添加得分触发器 在pipeDown0下创建空游戏对象,命名为scoreTrigger。
设置Position为(0,3,0),然后在scoreTrigger上添加Box Collider 2D组件,设置如图所示。
效果如图所示。
同理,在pipeDown1和pipeDown2下也创建scoreTrigger。最后效果如图所示。
2. 添加得分方法 在GameRoot中添加以下方法,代码如下。 public void GetPoint() { score++; txtScore.text = "Score:" + score; } 3. 添加得分触发方法 在GateGroup上创建ScoreCtrl脚本,代码如下。 using System.Collections; using System.Collections.Generic; using UnityEngine;
public class ScoreCtrl : MonoBehaviour {
public void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Player")) { GameRoot.Instance.GetPoint(); } } } 为了让这个方法对所有Trigger有效,在GateGroup上添加RigidBody2D组件,并设置组件,如图所示。
最后,修改Bird对象的Tag为Player。 任务4.5 失败 添加失败触发方法 在SportCtrl脚本里添加碰撞检测方法,代码如下。 void OnCollisionEnter2D(Collision2D coll) { GameRoot.Instance.End(); } 单元5 逻辑优化 任务5.1 控制优化 1. 禁用控制 修改SportCtrl.cs的FixedUpdate方法,代码如下。 void FixedUpdate() { if (GameRoot.Instance.GAMESTATE != GameRoot.GAMERUN) return; Fly(); } 2. 禁用和启用重力 在SportCtrl.cs中添加两个方法: public void EnableGravity() { rb.gravityScale = 0.7f; } public void DisEnableGravity() { rb.gravityScale = 0; } 然后添加SportCtrl单例并在游戏开始消除重力: public static SportCtrl Instance; void Start() { Instance = this; rb = GetComponent(); DisEnableGravity(); } 在GameRoot中调用: public void Ready() { SportCtrl.Instance.DisEnableGravity(); bird.position = Vector3.zero; StartWnd.gameObject.SetActive(false); ReadyWnd.gameObject.SetActive(true); GAMESTATE = GAMEREADY; bird.rotation = Quaternion.identity; }public void Run() { SportCtrl.Instance.EnableGravity(); ReadyWnd.gameObject.SetActive(false); GAMESTATE = GAMERUN; } 3. 禁用和启用速度 在SportCtrl里添加方法: public void Init() { rb.velocity = Vector3.zero; rb.angularVelocity = 0; } 在GameRoot里调用: public void Ready() { SportCtrl.Instance.Init(); SportCtrl.Instance.DisEnableGravity(); bird.position = Vector3.zero; StartWnd.gameObject.SetActive(false); ReadyWnd.gameObject.SetActive(true); GAMESTATE = GAMEREADY; bird.rotation = Quaternion.identity; } 任务5.2 场景重置 1. 在GameRoot游戏对象上添加MapManager脚本 using System.Collections; using System.Collections.Generic; using UnityEngine;
public class MapManager : MonoBehaviour {
public List maps = new List(); List pos = new List(); // Use this for initialization void Start () { for (var e in maps) { pos.Add(e.position); } }
public void Init () { for (int i = 0; i < maps.Count; i++) { maps[i].position = pos[i]; } } } 2. 在外部赋值
3. 在GameRoot里调用 public void Ready() { GetComponent(). Init (); SportCtrl.Instance. Init (); SportCtrl.Instance.DisEnableGravity(); bird.position = Vector3.zero; StartWnd.gameObject.SetActive(false); ReadyWnd.gameObject.SetActive(true); GAMESTATE = GAMEREADY; bird.rotation = Quaternion.identity; } 任务5.3 保存分数 1. 读取历史最高分 在GameRoot中添加最高分变量: public int best; 在游戏结束时,判断是否更新最高分: public void End() { if (GAMESTATE == GAMEEND) return; EndWnd.gameObject.SetActive(true); GAMESTATE = GAMEEND; int best=PlayerPrefs.GetInt("best"); if (score > best) { PlayerPrefs.SetInt("best", score); } } 2. 重置分数 在GameRoot里修改方法: public void Ready() { score = 0; GetComponent().Init(); SportCtrl.Instance. Init (); SportCtrl.Instance.DisEnableGravity(); bird.position = Vector3.zero; StartWnd.gameObject.SetActive(false); ReadyWnd.gameObject.SetActive(true); GAMESTATE = GAMEREADY; bird.rotation = Quaternion.identity; } 单元6 声音和动画 任务6.1 添加声音 1. 在GameRoot上添加AudioManager脚本 using System.Collections; using System.Collections.Generic; using UnityEngine;
public class AudioManager : MonoBehaviour { public static AudioManager Instance; public AudioSource fgSource; public AudioSource bgSource; public AudioClip wing; public AudioClip hit; public AudioClip die; public AudioClip point; public AudioClip swooshing;
void Start() { Instance = this; } public void PlayHit() { fgSource .PlayOneShot(hit); } public void PlayWing() { if (!fgSource.isPlaying) fgSource.PlayOneShot(wing); } public void PlayPoint() { bgSource.PlayOneShot(point); } public void PlaySwooshing() { fgSource .PlayOneShot(swooshing); } public void PlayDie() { fgSource .PlayOneShot(die); } } 在GameRoot下创建两个空物体fgsource和bgsource,均添加AudioSource组件。
2. 在外部对AudioManager进行赋值
3. 在GameRoot中调用 public void Ready() { AudioManager.Instance.PlaySwooshing(); score = 0; GetComponent(). Init (); SportCtrl.Instance. Init (); SportCtrl.Instance.DisEnableGravity(); bird.position = Vector3.zero; StartWnd.gameObject.SetActive(false); ReadyWnd.gameObject.SetActive(true); GAMESTATE = GAMEREADY; bird.rotation = Quaternion.identity; } public void End() { AudioManager.Instance.PlayHit(); if (GAMESTATE == GAMEEND) return; AudioManager.Instance.PlayDie();
EndWnd.gameObject.SetActive(true); GAMESTATE = GAMEEND; int best=PlayerPrefs.GetInt("best"); if (score > best) { PlayerPrefs.SetInt("best", score); } } public void GetPoint() { AudioManager.Instance.PlayPoint(); score++; //AudioSvc.Instance.Playpoint(); txtScore.text = "Score:" + score; } 4. 在SportCtrl中进行赋值 public void Fly() {
float xSpeed = 1f; Vector3 v = rb.velocity; float ySpeed = v.y; if (Input.GetMouseButton(0)) { ySpeed = 2f; AudioManager.Instance.PlayWing(); } rb.velocity = new Vector3(xSpeed, ySpeed, 0); } 任务6.2 添加动画 1. 添加图片 在SportCtrl脚本中添加图片列表: public SpriteRenderer renderer; public List sprites=new List(); int index=0; float timer=0; 2. 在外部赋值
3. 切换图片 void SwitchSprite(float interval) { timer += Time.fixedDeltaTime; if (timer > interval) { timer = 0; index = (index + 1) % sprites.Count; renderer.sprite = sprites[index]; } } // Update is called once per frame void FixedUpdate() { if (GameRoot.Instance.GAMESTATE != GameRoot.GAMERUN) return; Fly(); SwitchSprite(0.1f); }
|
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |